In many information processing applications, a server application running on a host or server computer in a distributed network provides processing services or functions for client applications running on terminal or workstation computers of the network which are operated by a multitude of users. Common examples of such server applications include software for processing class registrations at a university, travel reservations, money transfers and other services at a bank, and sales at a business. In these examples, the processing services provided by the server application may update databases of class schedules, hotel reservations, account balances, order shipments, payments, or inventory for actions initiated by the individual users at their respective stations.
An important consideration in many of these server applications is the ability to handle heavy processing loads. In the above server application examples, for instance, the updates for a large number of users may be submitted to the server application within a short time period. As each update may consume significant processing time, each additional user of the server application can slow the response or time to complete updates for all other users' updates, thus reducing the quality of service for all users. Eventually, the load may exceed the processing capacity, possibly resulting in system failure, down time, and lost data. The degree to which a server application can support incremental increases in user load while preserving performance is sometimes referred to as scalability.
One factor that affects server application scalability is the server application's use of memory to store user data while performing processing for that user. The server computer's memory is a limited resource that is shared among all the users of the server application. Because server computer memory is a shared fixed resource, the duration that the server application stores one user's data affects the availability of the memory to store other users' data. By minimizing the duration that data is stored in the server computer memory for each user, the server application is able to support many more clients with a given server computer memory capacity.
One approach to enhancing scalability is for the server application to keep user data in memory only during the course of a single interaction or exchange with a client (e.g., while processing one remote procedure call or message exchange from the client). The server application keeps a current state of each user's data (also referred to as the user's “state”) in secondary storage (e.g., hard disk and other large capacity storage devices), and loads the state into the server computer's main memory only as long as necessary to perform a discrete processing operation on the data responsive to the single interaction. After completing processing of the single interaction, the server application again stores the state into secondary memory. This practice effectively shares the scarce main memory resources among the users. Using this practice, the server application can accommodate more users.
Under this “surgical strike” or “get in/get out” style of programming, the server application generally consisted of a group of functions or procedures that could be called remotely by client applications at the user's workstations to perform the discrete processing operations in a single interaction between the client and server application. In general, the user's state was loaded into main memory at the start of the function, and stored away at the function's completion. Also, the function's parameter list would contain all input data from the client application that was needed for the processing operation. This would sometimes lead to server application functions with extensive parameter lists. For example, a simple function in a banking server application for a money transfer might include parameters for the amount to be transferred, the account number to debit, the account number to credit, the authorization number, the check number, the teller id, the branch id, etc.
Programming models generally known as object-oriented programming provide many benefits that have been shown to increase programmers' productivity, but are in many ways antithetical to the just discussed approach to enhancing scalability. In object-oriented programming, programs are written as a collection of object classes which each model real world or abstract items by combining data to represent the item's properties with functions to represent the item's functionality. More specifically, an object is an instance of a programmer-defined type referred to as a class, which exhibits the characteristics of data encapsulation, polymorphism and inheritance. Data encapsulation refers to the combining of data (also referred to as properties of an object) with methods that operate on the data (also referred to as member functions of an object) into a unitary software component (i.e., the object), such that the object hides its internal composition, structure and operation and exposes its functionality to client programs that utilize the object only through one or more interfaces. An interface of the object is a group of semantically related member functions of the object. In other words, the client programs do not access the object's data directly, but must instead call functions on the object's interfaces to operate on the data.
Polymorphism refers to the ability to view (i.e., interact with) two similar objects through a common interface, thereby eliminating the need to differentiate between two objects. Inheritance refers to the derivation of different classes of objects from a base class, where the derived classes inherit the properties and characteristics of the base class.
Object-oriented programming generally has advantages in ease of programming, extensibility, reuse of code, and integration of software from different vendors and (in some object-oriented programming models) across programming languages. However, object-oriented programming techniques generally are antithetical to the above-discussed approach to enhancing server application scalability by minimizing the duration of state to single client/server application interactions. In object-oriented programming, the client program accesses an object by obtaining a pointer or reference to an instance of the object in memory. The client program retains this object reference throughout the course of its interaction with the object, which allows the client program to call member functions on the object. So long as any client programs have a reference to an object's instance, data associated with the instance is maintained in memory to avoid the client issuing a call to an invalid memory reference. At the very least, even where the client program calls only a single member function, the object instance is kept in memory between the client program's initial call to request a reference to the object instance and the client program's call to release the reference (between which the client program issues one or more calls to member functions of the object using the reference). In other words, the client program has control over the object's lifetime. The object is kept in memory until the client's reference to the object is released.
Also, object-oriented programming encourages setting an object's properties using separate member functions. For example, a money transfer object may provide a set of member functions that includes a SetDebitAccount( ) function, a SetCreditAccount( ) function, a SetTransferAmount( ) function, etc. that the client program calls to set the object's properties. Finally, the client program may call a TransferMoney( ) function to cause the object to perform the money transfer operation using the accumulated object properties (also referred to as the object's state). Again, while the client program issues these separate calls, the object is maintained in memory. In a server application, this programming style can drastically reduce the server application's scalability.
A further disadvantage of object-oriented programming of server applications is that each separate operation with or use of an object often requires creating a separate instance of the object. This is because the accumulated properties that are set for one operation with an object typically differ from the settings of the properties in another operation. In the above money transfer object example, for instance, separate money transfer operations usually involve different account numbers and transfer amounts. Since the accumulated state of an object is retained, the client program either instantiates a new instance of the object for a subsequent money transfer or carefully resets each property of the previously used object instance to avoid carrying properties set in the previous money transfer over to the subsequent transfer. However, instantiating each object also is expensive in terms of processing time and thus further reduces server application scalability.
The present invention is a component-based server application framework using an object-oriented programming model that supports control by server application components over state duration to enhance server application scalability. Server applications in the component-based framework are programmed as software components that conform to object-oriented programming principles (e.g., encapsulation of user state). Additionally, the framework supports destruction of user state encapsulated as an instance of a server application component under control of the server application component. As opposed to conventional object-oriented programming models where state duration is controlled solely by the client releasing its reference to the server application component instance, the component-based server application framework allows server application programmers to minimize state duration so as to enhance server application scalability.
According to one aspect of the invention, the framework provides a component integration interface or application programming interface (“API”) with which the component indicates upon return from a client program's call that the component does not need to maintain its state after return from the call. After receiving this indication, the framework destroys the component's state when the component thereafter returns from the client program call, such as by causing the instance of the component to be destroyed or by causing the instance to be disassociated from the client reference to the component (and its state reset).
According to another aspect of the invention, the framework provides for server application component control over state duration in connection with transaction processing. Transaction processing is a well known technique for processing a collection of actions (referred to as a “transaction”) as a single indivisible unit of work in accordance with the properties of atomicity, isolation, and durability (referred to as the “ACID” properties). (See, inter alia, Orfali, Robert et al., Essential Client/Server Survival Guide, John Wiley & Sons, Inc., at 241–260 (1994).) When the component completes its participation in a transaction, the component calls framework-provided interfaces, such as a SetComplete( ) or SetAbort( ) function, to notify the framework that the component's work on the transaction is successfully completed or must be aborted, and further that the component's state can be released, In response, the framework destroys the component's state. This helps to free server memory and other server resources used by the component as quickly as possible after the component completes its work in a transaction.
Sever application component control over state durability according to the invention also can enhance the scalability of server applications outside of a transaction. For example, a stateless component (one which does not retain state between calls from a client program) can call the SetComplete or SetAbort functions before returning from each client call to the component. This causes the transaction server executive to release component state immediately upon return from the client's call. The component's state thus is not left consuming resources on return from the client's call, while awaiting its final release by the client program. This becomes increasingly effective at reducing server resource consumption as the time to communicate between the client and server computer is increased (e.g., in distributed networks with low bandwidth or high latency connections between client and server computers).
Additional features and advantages of the invention will be made apparent from the following detailed description of an illustrated embodiment which proceeds with reference to the accompanying drawings.